Skip to content

perf: use pre-fetched advisory severity summary on SBOM list#1127

Merged
ctron merged 2 commits into
guacsec:mainfrom
ctron:feature/sbom-advisory-summary
Jul 9, 2026
Merged

perf: use pre-fetched advisory severity summary on SBOM list#1127
ctron merged 2 commits into
guacsec:mainfrom
ctron:feature/sbom-advisory-summary

Conversation

@ctron

@ctron ctron commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Consume the new ?advisories=true query parameter on the SBOM list endpoint to display severity counts per row
  • Eliminates N+1 requests to GET /sbom/{id}/advisory (~4s each) that were made per table row

Dependencies

Depends on backend PR: guacsec/trustify#2437 which adds the ?advisories=true query parameter to GET /v3/sbom.

Changes

  • SbomVulnerabilities.tsx - rewritten to accept pre-fetched advisory summary instead of fetching per row
  • sboms.ts - pass advisories: true in the list query
  • sbom-table.tsx - pass item.advisories to the component instead of item.id
  • sbom-context.ts - add advisories field to the item type
  • types.gen.ts - not committed (gitignored), will be regenerated from the updated OpenAPI spec

Test plan

  • SBOM list page loads severity badges without per-row API calls
  • Severity counts match those from individual GET /sbom/{id}/advisory responses
  • Page load time significantly reduced (from ~40s N+1 to single batch query)

🤖 Generated with Claude Code

Summary by Sourcery

Optimize SBOM list vulnerability display by consuming pre-fetched advisory summaries instead of per-row advisory API calls.

New Features:

  • Display SBOM vulnerability severities from advisory summaries returned by the SBOM list endpoint.

Enhancements:

  • Extend SBOM list items with optional advisory summary data and propagate it through the table context to the vulnerability gallery component.
  • Include the advisories flag in SBOM list queries to retrieve aggregated advisory severity data in a single request.

@sourcery-ai

sourcery-ai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Frontend now consumes pre-fetched advisory severity summaries from the SBOM list API (via the new ?advisories=true parameter) and passes them through context and table components to SBOMVulnerabilities, removing per-row advisory fetches and rendering severity badges directly from the list response.

Sequence diagram for SBOM list advisories prefetch

sequenceDiagram
    actor User
    participant SbomListPage
    participant useFetchSBOMs
    participant Backend as Backend_v3_sbom_API
    participant SbomTable
    participant SBOMVulnerabilities
    participant VulnerabilityGallery

    User ->> SbomListPage: Open SBOM list page
    SbomListPage ->> useFetchSBOMs: useFetchSBOMs(params)
    useFetchSBOMs ->> Backend: GET /v3/sbom?advisories=true
    Backend -->> useFetchSBOMs: SBOM list with advisories
    useFetchSBOMs -->> SbomListPage: items[{ id, advisories, ... }]
    SbomListPage ->> SbomTable: Render with items
    SbomTable ->> SBOMVulnerabilities: SBOMVulnerabilities(advisories)
    SBOMVulnerabilities ->> VulnerabilityGallery: VulnerabilityGallery(severities)
    Note over SBOMVulnerabilities,VulnerabilityGallery: No per-row GET /sbom/{id}/advisory requests
Loading

File-Level Changes

Change Details Files
SBOM vulnerabilities cell now renders from pre-fetched advisory summary instead of performing per-row API calls.
  • Change SBOMVulnerabilities props to accept an optional advisories summary object instead of an SBOM ID.
  • Remove usage of useVulnerabilitiesOfSbom and related loading/error UI, replacing it with a direct call to VulnerabilityGallery.
  • Introduce a DEFAULT_SEVERITIES map keyed by ExtendedSeverity and merge it with the provided advisories before rendering.
client/src/app/pages/sbom-list/components/SbomVulnerabilities.tsx
SBOM list data model and table wiring now carry advisory summaries from the list query into the vulnerabilities cell.
  • Extend the SBOM search/list context item type to include an optional advisories field with type SbomAdvisorySummary.
  • Update the SBOM table to pass item.advisories into SBOMVulnerabilities instead of item.id.
  • Ensure type imports include SbomAdvisorySummary alongside existing SBOM-related types.
client/src/app/pages/sbom-list/sbom-context.ts
client/src/app/pages/sbom-list/sbom-table.tsx
SBOM list query requests advisory summaries in bulk via the new backend flag.
  • Update the useFetchSBOMs query params to include advisories: true so the backend returns advisory severity summaries with each SBOM list item.
client/src/app/queries/sboms.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • Consider explicitly handling the case where item.advisories is undefined or null in SBOMVulnerabilities (e.g., via a fallback UI or prop type refinement) so the table cell behavior is clear when advisory data is missing.
  • The severities object in SBOMVulnerabilities is recreated on every render via object spread; if this component is used in a large table, memoizing or building it earlier (e.g., in the parent or via useMemo) could avoid unnecessary re-renders of VulnerabilityGallery.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider explicitly handling the case where `item.advisories` is `undefined` or `null` in `SBOMVulnerabilities` (e.g., via a fallback UI or prop type refinement) so the table cell behavior is clear when advisory data is missing.
- The `severities` object in `SBOMVulnerabilities` is recreated on every render via object spread; if this component is used in a large table, memoizing or building it earlier (e.g., in the parent or via `useMemo`) could avoid unnecessary re-renders of `VulnerabilityGallery`.

## Individual Comments

### Comment 1
<location path="client/src/app/pages/sbom-list/components/SbomVulnerabilities.tsx" line_range="23" />
<code_context>
+  advisories,
 }) => {
-  const { data, isFetching, fetchError } = useVulnerabilitiesOfSbom(sbomId);
+  const severities = { ...DEFAULT_SEVERITIES, ...advisories };

-  return (
</code_context>
<issue_to_address>
**issue (bug_risk):** Spreading a possibly null/undefined `advisories` object can throw at runtime.

Because `advisories` is now optional (`advisories?: SbomAdvisorySummary | null`), spreading it directly in `{ ...DEFAULT_SEVERITIES, ...advisories }` will throw if it’s `null` or `undefined`. Please guard or default it, e.g. `const severities = { ...DEFAULT_SEVERITIES, ...(advisories ?? {}) };`, or handle the missing data before rendering `VulnerabilityGallery`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread client/src/app/pages/sbom-list/components/SbomVulnerabilities.tsx Outdated
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.30%. Comparing base (058cf8b) to head (ede212f).

Files with missing lines Patch % Lines
client/src/app/components/VulnerabilityGallery.tsx 0.00% 2 Missing ⚠️
...pages/sbom-list/components/SbomVulnerabilities.tsx 0.00% 1 Missing ⚠️
client/src/app/queries/sboms.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1127      +/-   ##
==========================================
- Coverage   53.33%   53.30%   -0.04%     
==========================================
  Files         270      270              
  Lines        5880     5878       -2     
  Branches     1842     1845       +3     
==========================================
- Hits         3136     3133       -3     
- Misses       2443     2444       +1     
  Partials      301      301              
Flag Coverage Δ
unit 7.02% <0.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ctron ctron force-pushed the feature/sbom-advisory-summary branch 3 times, most recently from 51635f1 to c07c1d9 Compare July 3, 2026 13:46

import { LoadingWrapper } from "@app/components/LoadingWrapper";
import { TableCellError } from "@app/components/TableCellError";
import type { SbomAdvisorySummary } from "@app/client";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the SbomAdvisorySummary type in openapi spec, there are only SbomAdvisory (that was used in getSbomAdvisories before) or a new type RequestedField_HashMap_HashMap (as was added in this commit ).

Comment on lines +11 to +23
const DEFAULT_SEVERITIES: { [key in ExtendedSeverity]: number } = {
unknown: 0,
none: 0,
low: 0,
medium: 0,
high: 0,
critical: 0,
};

export const SBOMVulnerabilities: React.FC<SBOMVulnerabilitiesProps> = ({
sbomId,
advisories,
}) => {
const { data, isFetching, fetchError } = useVulnerabilitiesOfSbom(sbomId);
const severities = { ...DEFAULT_SEVERITIES, ...advisories };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the better pattern would be not to add const DEFAULT_SEVERITIES where everything is equals to zero but to change VulnerabilityGalleryProps from this:

interface VulnerabilityGalleryProps {
  severities: { [key in ExtendedSeverity]: number };
}

To this:

interface VulnerabilityGalleryProps {
  severities: Partial<{ [key in ExtendedSeverity]: number }>;
}

This would let us keep SBOMVulnerabilities component clean and just use it like that:

export const SBOMVulnerabilities: React.FC<SBOMVulnerabilitiesProps> = ({ advisories }) => (
  <VulnerabilityGallery severities={advisories ?? {}} />
);


import type { SbomHead, SourceDocument } from "@app/client";
import type {
SbomAdvisorySummary,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here as in the previous comment

I don't see the SbomAdvisorySummary type in openapi spec, there are only SbomAdvisory (that was used in getSbomAdvisories before) or a new type RequestedField_HashMap_HashMap

@ctron ctron added the backport release/0.5.z This PR should be backported to release/0.5.z branch. label Jul 6, 2026
ctron added a commit to ctron/trustify-ui that referenced this pull request Jul 6, 2026
Use generated RequestedFieldHashMapHashMap type instead of
non-existent SbomAdvisorySummary, make VulnerabilityGallery
accept partial severities, and simplify SBOMVulnerabilities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ctron

ctron commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@stanislavsemeniuk tried to convince claude to fix the review comments. Maybe you can have another look.

@ctron

ctron commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@stanislavsemeniuk It still fails, but neither claude nor me have an idea why. We need help.

@stanislavsemeniuk

stanislavsemeniuk commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

@ctron The e2e test is failing because it expects quarkus-bom SBOM to have total amount of vulnerabilities to be exactly 16. But backend returns 21

{
  "medium": 16,
  "high": 4,
  "low": 1
}

To be honest i don't know why it expects 16 and why BE returns 21, but if 21 is correct number we can just change '16' to '21' here in e2e/tests/ui/pages/sbom-list/columns.spec.ts :

await expect(
      table._table
        .locator(`td[data-label="Vulnerabilities"]`)
        .locator("div[aria-label='total']", { hasText: "16" }), //here from 16 to 21
    ).toHaveCount(1);

And also about RequestedFieldHashMapHashMap. Is the name of this type is intentional? Because for me it feels like some kind of a claude error on a BE side or maybe it is just my lack of knowledge

@ctron

ctron commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for helping out with this. Neither claude nor me came to that conclusion 😬 I found an issue in the backend, which explains that discrepancy, I'll be going to fix it and then we can test this again.

ctron added a commit to ctron/trustify-ui that referenced this pull request Jul 8, 2026
Use generated RequestedFieldHashMapHashMap type instead of
non-existent SbomAdvisorySummary, make VulnerabilityGallery
accept partial severities, and simplify SBOMVulnerabilities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ctron ctron force-pushed the feature/sbom-advisory-summary branch from 591915b to d58b530 Compare July 8, 2026 08:46
ctron added a commit to ctron/trustify-ui that referenced this pull request Jul 8, 2026
Use generated RequestedFieldHashMapHashMap type instead of
non-existent SbomAdvisorySummary, make VulnerabilityGallery
accept partial severities, and simplify SBOMVulnerabilities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ctron ctron force-pushed the feature/sbom-advisory-summary branch from d58b530 to 0402418 Compare July 8, 2026 09:08
ctron and others added 2 commits July 8, 2026 17:02
Consume the new `?advisories=true` query parameter on the SBOM list
endpoint to display severity counts per row, eliminating N+1 requests
to `GET /sbom/{id}/advisory` (~4s each).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use generated RequestedFieldHashMapHashMap type instead of
non-existent SbomAdvisorySummary, make VulnerabilityGallery
accept partial severities, and simplify SBOMVulnerabilities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ctron ctron force-pushed the feature/sbom-advisory-summary branch from 0402418 to ede212f Compare July 8, 2026 15:02
@ctron ctron enabled auto-merge July 8, 2026 15:02
@ctron ctron added this pull request to the merge queue Jul 9, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 9, 2026
@ctron ctron added this pull request to the merge queue Jul 9, 2026
Merged via the queue into guacsec:main with commit fb9c2cf Jul 9, 2026
30 of 33 checks passed
@github-project-automation github-project-automation Bot moved this to Done in Trustify Jul 9, 2026
@trustify-ci-bot

Copy link
Copy Markdown
Contributor

Backport failed for release/0.5.z, because it was unable to cherry-pick the commit(s).

Please cherry-pick the changes locally and resolve any conflicts.

git fetch origin release/0.5.z
git worktree add -d .worktree/backport-1127-to-release/0.5.z origin/release/0.5.z
cd .worktree/backport-1127-to-release/0.5.z
git switch --create backport-1127-to-release/0.5.z
git cherry-pick -x fb9c2cf05c3d6442db377a885f9b03a03618d109

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport release/0.5.z This PR should be backported to release/0.5.z branch.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants